Dynamically Loading Assemblies

In the previous chapter, you learned all about how the CLR consults the assembly manifest when probing for an externally referenced assembly. However, there will be many times when you need to load assemblies on the fly programmatically, even if there is no record of said assembly in the manifest. Formally speaking, the act of loading external assemblies on demand is known as a dynamic load.

System.Reflection defines a class named Assembly. Using this class, you are able to dynamically load an assembly as well as discover properties about the assembly itself. Using the Assembly type, you are able to dynamically load private or shared assemblies, as well as load an assembly located at an arbitrary location. In essence, the Assembly class provides methods (Load() and LoadFrom() in particular) that allow you to programmatically supply the same sort of information found in a client-side *.config file.

To illustrate dynamic loading, create a brand-new Console Application named ExternalAssemblyReflector. Your task is to construct a Main() method that prompts for the friendly name of an assembly to load dynamically. You will pass the Assembly reference into a helper method named DisplayTypes(), which will simply print the names of each class, interface, structure, enumeration, and delegate it contains. The code is refreshingly simple:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;
using System.IO; // For FileNotFoundException definition.

namespace ExternalAssemblyReflector
{
    class Program
    {
        static void DisplayTypesInAsm(Assembly asm)
        {
            Console.WriteLine("\n***** Types in Assembly *****");
            Console.WriteLine("->{0}", asm.FullName);
            Type[] types = asm.GetTypes();
            foreach (Type t in types)
                Console.WriteLine("Type: {0}", t);
            Console.WriteLine("");
        }
        
        static void Main(string[] args)
        {
            Console.WriteLine("***** External Assembly Viewer *****");
        
            string asmName = "";
            Assembly asm = null;
        
            do
            {
                Console.WriteLine("\nEnter an assembly to evaluate");
                Console.Write("or enter Q to quit: ");

                // Get name of assembly.
                asmName = Console.ReadLine();

                // Does user want to quit?
                if (asmName.ToUpper() == "Q")
                {
                    break;
                }
                
                // Try to load assembly.
                try
                {
                    asm = Assembly.Load(asmName);
                    DisplayTypesInAsm(asm);
                }        
                catch
                {
                    Console.WriteLine("Sorry, can't find assembly.");
                }
            } while (true);
        }
    }
}

Notice that the static Assembly.Load() method has been passed only the friendly name of the assembly you are interested in loading into memory. Thus, if you wish to reflect over CarLibrary.dll, you will need to copy the CarLibrary.dll binary to the \bin\Debug directory of the ExternalAssemblyReflector application to run this program. Once you do, you will find output similar to the following:

***** External Assembly Viewer *****

Enter an assembly to evaluate
or enter Q to quit: CarLibrary

***** Types in Assembly *****
->CarLibrary, Version=2.0.0.0, Culture=neutral, PublicKeyToken=33a2bc294331e8b9
Type: CarLibrary.MusicMedia
Type: CarLibrary.EngineState
Type: CarLibrary.Car
Type: CarLibrary.SportsCar
Type: CarLibrary.MiniVan

If you wish to make ExternalAssemblyReflector more flexible, you can update your code to load the external assembly using Assembly.LoadFrom() rather than Assembly.Load():

try
{
    asm = Assembly.LoadFrom(asmName);
    DisplayTypesInAsm(asm);
}

By doing so, you can enter an absolute path to the assembly you wish to view (e.g., C:\MyApp\MyAsm.dll). Essentially, Assembly.LoadFrom() allows you to programmatically supply a <codeBase> value. With this adjustment, you can now pass in a full path to your console application. Thus, if CarLibrary.dll was located under C:\MyCode, you could enter the following:

***** External Assembly Viewer *****

Enter an assembly to evaluate
or enter Q to quit: C:\MyCode\CarLibrary.dll

***** Types in Assembly *****
->CarLibrary, Version=2.0.0.0, Culture=neutral, PublicKeyToken=33a2bc294331e8b9
Type: CarLibrary.EngineState
Type: CarLibrary.Car
Type: CarLibrary.SportsCar
Type: CarLibrary.MiniVan

Source Code The ExternalAssemblyReflector project is included in the Chapter 15 subdirectory.